﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.ServiceModel;
using System.ServiceModel.Discovery;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Threading;
using System.Diagnostics;

namespace WindSim
{
    [CallbackBehavior(ConcurrencyMode = ConcurrencyMode.Multiple, UseSynchronizationContext = false)]
    public partial class WindSimController : BaseClient, INotifyPropertyChanged, SimServiceReference.SimulationServiceCallback
    {
        //Locking
        public object locker = new object { };

        //Delegates
        private delegate void Action();
        private delegate void ControlAction(object data);

        //RoutedCommands
        public static RoutedCommand ManualControlCommand = new RoutedCommand();            

        //Properties & Fields
        private string _clientName = "Model";
        private Guid _guid = Guid.NewGuid();
        private SimServiceReference.ClientType _clientType = SimServiceReference.ClientType.Controller;
        private bool _isConnected = false;
        private bool _isSearching = true;
        public override string ClientName
        {
            get { return _clientName; }
            set { _clientName = value; }
        }
        public override Guid ClientGuid
        {
            get { return _guid; }
        }
        public override SimServiceReference.ClientType ClientType
        {
            get { return _clientType; }
        }
        public override bool IsConnected
        {
            get { return _isConnected; }
            set
            {
                _isConnected = value;
                NotifyPropertyChanged("IsConnected");
                if (value)
                {
                    _client.RegisterClient(_guid, _clientType, null);
                    lbConnectedModels.ItemsSource = ConnectedModels;
                }
            }
        }
        public override bool IsSearching
        {
            get { return _isSearching; }
            set { _isSearching = value; NotifyPropertyChanged("IsSearching"); }
        }

        private string _statusbarInfo = String.Empty;
        public string StatusbarInfo
        {
            get { return _statusbarInfo; }
            set { _statusbarInfo = value; NotifyPropertyChanged("StatusbarInfo"); }
        }

        //Connected Models
        public ObservableDictionary<Guid, SimServiceReference.ModelData> ConnectedModels;
        public Dictionary<Guid, Thread> ControlThreads;

        //Constructor
        public WindSimController()
        {
            InitializeComponent();
            ConnectedModels = new ObservableDictionary<Guid, SimServiceReference.ModelData>();
            ControlThreads = new Dictionary<Guid, Thread>();

            ManualControlCommand.InputGestures.Add(new KeyGesture(Key.A, ModifierKeys.Control));
        }

        //Automation
        public void Automatic1(object data)
        {
            try
            {
                InvokeAction(() => { StatusbarInfo = "Automatic1 gestartet"; });
                Guid id = (Guid)data;
                Random randDirection = new Random();
                Random randSpeed = new Random();

                ConnectedModels[id].PowerstateController = SimServiceReference.PowerState.On;
                Thread.Sleep(100);

                for (int i = 0; i < 400; i++)
                {
                    TimeSpan ts = TimeSpan.FromMinutes(i * (1440.0 / 400.0));
                    DateTime DayTime = new DateTime(1, 1, 1, ts.Hours, ts.Minutes, 0);
                    double ws = i * (30.0 / 400.0);
                    double wd = i * (360.0 / 400.0);

                    _client.SetDayTimeAtModel(id, DayTime);
                    ConnectedModels[id].DayTime = DayTime;

                    _client.SetWindSpeedAtModel(id, ws);
                    ConnectedModels[id].WindSpeed = ws;

                    _client.SetWindDirectionAtModel(id, wd);
                    ConnectedModels[id].WindDirection = wd;
                    Thread.Sleep(125);
                }

                _client.SetFunctionStateAtModel(id, SimServiceReference.FunctionState.Error);
                ConnectedModels[id].FunctionstateModel = SimServiceReference.FunctionState.Error;
                Thread.Sleep(2000);

                _client.SetFunctionStateAtModel(id, SimServiceReference.FunctionState.OK);
                ConnectedModels[id].FunctionstateModel = SimServiceReference.FunctionState.OK;
                Thread.Sleep(500);

                ConnectedModels[id].PowerstateController = SimServiceReference.PowerState.Off;
                _client.SetPowerStateAtModel(id, SimServiceReference.PowerState.Off);
                InvokeAction(() => { StatusbarInfo = "Automatic1 beendet"; });

                _client.SetControlTypeAtModel(id,SimServiceReference.SimControlType.Manual);
                ConnectedModels[id].ControlType = SimServiceReference.SimControlType.Manual;
            }
            catch (ThreadAbortException)
            {
                InvokeAction(() => { StatusbarInfo = "Automatic1 abgebrochen"; });
            }
        }
        public void Automatic2(object data)
        {
            Guid id = (Guid)data;
            try
            {
                InvokeAction(() => { StatusbarInfo = "Automatic2 gestartet"; });
                Random randDirection = new Random();
                Random randSpeed = new Random();
                Random randTime = new Random();

                ConnectedModels[id].PowerstateController = SimServiceReference.PowerState.On;
                Thread.Sleep(100);

                for (int i = 0; i < 10; i++)
                {
                    int ws = randSpeed.Next(0, 30);
                    int wd = randDirection.Next(0, 360);
                    DateTime DayTime = new DateTime(1, 1, 1, randTime.Next(0,23), randTime.Next(0,59), 0);

                    _client.SetDayTimeAtModel(id, DayTime);
                    ConnectedModels[id].DayTime = DayTime;

                    _client.SetWindSpeedAtModel(id, ws);
                    ConnectedModels[id].WindSpeed = ws;

                    _client.SetWindDirectionAtModel(id, wd);
                    ConnectedModels[id].WindDirection = wd;
                    Thread.Sleep(4000);
                }
                _client.SetFunctionStateAtModel(id, SimServiceReference.FunctionState.Error);
                ConnectedModels[id].FunctionstateModel = SimServiceReference.FunctionState.Error;
                Thread.Sleep(2000);

                _client.SetFunctionStateAtModel(id, SimServiceReference.FunctionState.OK);
                ConnectedModels[id].FunctionstateModel = SimServiceReference.FunctionState.OK;

                Thread.Sleep(500);

                ConnectedModels[id].PowerstateController = SimServiceReference.PowerState.Off;
                _client.SetPowerStateAtModel(id, SimServiceReference.PowerState.Off);

                InvokeAction(() => { StatusbarInfo = "Automatic1 beendet"; });

                _client.SetControlTypeAtModel(id, SimServiceReference.SimControlType.Manual);
                ConnectedModels[id].ControlType = SimServiceReference.SimControlType.Manual;
            }
            catch (ThreadAbortException)
            {
                InvokeAction(() => { StatusbarInfo = "Automatic2 abgebrochen"; });
            }

        }

        //Callbacks
        public void NotifyControllerModelRegistered(Guid Id, SimServiceReference.ModelData modelData)
        {
            InvokeAction(() =>
            {
                ConnectedModels.Add(Id, new SimServiceReference.ModelData()
                {
                    Name = modelData.Name,
                    Location = modelData.Location,
                    RPM = modelData.RPM,
                    Energy = modelData.Energy,
                    FunctionstateModel = modelData.FunctionstateModel,
                    PowerstateModel = modelData.PowerstateModel,
                    PowerstateController = modelData.PowerstateModel,
                    ControlType = modelData.ControlType,
                    DayTime = modelData.DayTime
                });
                ControlThreads.Add(Id, new Thread(() => { }));
            });
        }
        public void NotifyControllerModelUnregistered(Guid Id)
        {
            InvokeAction(() => 
            { 
                ConnectedModels.Remove(Id);
                ControlThreads.Remove(Id);
            });
        }
        public void NotifyControllerPowerStateChanged(Guid Id, SimServiceReference.PowerState State)
        {
            lock(locker)
            {
                ConnectedModels[Id].PowerstateModel = State;
            }
        }
        public void NotifyControllerFunctionStateChanged(Guid Id, SimServiceReference.FunctionState State)
        {
            lock (locker)
            {
                ConnectedModels[Id].FunctionstateModel = State;
            }
        }
        public void NotifyControllerRPMChanged(Guid Id, double RPM)
        {
            lock (locker)
            {
                ConnectedModels[Id].RPM = RPM;
            }
        }
        public void NotifyControllerEnergyChanged(Guid Id, double Energy)
        {
            lock (locker)
            {
                ConnectedModels[Id].Energy = Energy;
            }
        }
        public void NotifyControllerNameChanged(Guid Id, string Name)
        {
            lock (locker)
            {
                ConnectedModels[Id].Name = Name;
            }
        }
        public void NotifyControllerLocationChanged(Guid Id, string Location)
        {
            lock (locker)
            {
                ConnectedModels[Id].Location = Location;
            }
        }
        #region Hier nicht implementierte Methoden (nur für Modell benötigt)
        public void NotifyModelPowerStateChanged(SimServiceReference.PowerState State)
        {
            throw new NotImplementedException("Wird nur vom Modell aufgerufen!");
        }
        public void NotifyModelWindDirectionChanged(double DirectionAngle)
        {
            throw new NotImplementedException("Wird nur vom Modell aufgerufen!");
        }
        public void NotifyModelWindSpeedChanged(double Speed)
        {
            throw new NotImplementedException("Wird nur vom Modell aufgerufen!");
        }
        public void NotifyModelFunctionStateChanged(SimServiceReference.FunctionState State)
        {
            throw new NotImplementedException("Wird nur vom Modell aufgerufen!");
        }
        public void NotifyModelDayTimeChanged(DateTime Time)
        {
            throw new NotImplementedException("Wird nur vom Modell aufgerufen!");
        }
        public void NotifyModelControlTypeChanged(SimServiceReference.SimControlType ControlType)
        {
            throw new NotImplementedException("Wird nur vom Modell aufgerufen!");
        }
        #endregion

        //Methods
        private void TryAddNewModel()
        {
            try
            {
                if (File.Exists(@"..\..\..\WindSimModel\bin\Release\WindSimModel.exe"))
                {
                    Process p = new Process();
                    p.StartInfo.FileName = @"..\..\..\WindSimModel\bin\Release\WindSimModel.exe";
                    p.Start();                
                }
                else if (File.Exists(@"..\WindSimModel\WindSimModel.exe"))
                {
                    Process p = new Process();
                    p.StartInfo.FileName = @"..\WindSimModel\WindSimModel.exe";
                    p.Start(); 
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show("Hinzufügen eines neuen Modells fehlgeschlagen.");
            }
        }

        //Eventhandler
        private void Window_Loaded_1(object sender, RoutedEventArgs e)
        {

            //ohne Discovery
            //_syncContext = SynchronizationContext.Current;
            //InstanceContext callbackContext = new InstanceContext(this);
            //_client = new SimServiceReference.SimulationServiceClient(callbackContext);
            //_client.RegisterClient(_guid, _clientType, null);
            //lbConnectedModels.ItemsSource = ConnectedModels;

            //mit Discovery
            DiscoverService();
        }
        private void Window_Closing_1(object sender, CancelEventArgs e)
        {
            try
            {
                _client.UnRegisterClient(_guid, _clientType);
            }
            catch { }

        }
        private void rbtnAddModel_Click(object sender, RoutedEventArgs e)
        {
            TryAddNewModel();
        }
        private void tgbtnAnAus_Checked(object sender, RoutedEventArgs e)
        {
            try
            {
                KeyValuePair<Guid, SimServiceReference.ModelData> selected = (KeyValuePair<Guid, SimServiceReference.ModelData>)lbConnectedModels.SelectedItem;
                _client.SetPowerStateAtModel((selected.Key), SimServiceReference.PowerState.On);
            }
            catch { }
        }
        private void tgbtnAnAus_Unchecked(object sender, RoutedEventArgs e)
        {
            try
            {
                KeyValuePair<Guid, SimServiceReference.ModelData> selected = (KeyValuePair<Guid, SimServiceReference.ModelData>)lbConnectedModels.SelectedItem;
                _client.SetPowerStateAtModel((selected.Key), SimServiceReference.PowerState.Off);
            }
            catch {}
        }
        private void sldWindSpeed_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            try
            {
                KeyValuePair<Guid, SimServiceReference.ModelData> selected = (KeyValuePair<Guid, SimServiceReference.ModelData>)lbConnectedModels.SelectedItem;
                _client.SetWindSpeedAtModel((selected.Key), e.NewValue);
            }
            catch { }
        }
        private void sldWindDirection_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            try
            {
                KeyValuePair<Guid, SimServiceReference.ModelData> selected = (KeyValuePair<Guid, SimServiceReference.ModelData>)lbConnectedModels.SelectedItem;
                _client.SetWindDirectionAtModel(selected.Key, e.NewValue);
            }
            catch {}
        }
        private void dtudDayTime_ValueChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
        {
            try
            {
                KeyValuePair<Guid, SimServiceReference.ModelData> selected = (KeyValuePair<Guid, SimServiceReference.ModelData>)lbConnectedModels.SelectedItem;
                _client.SetDayTimeAtModel(selected.Key, (DateTime)e.NewValue);
                selected.Value.DayTime = (DateTime)e.NewValue;
            }
            catch { }
        }
        private void btnStoerung_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                KeyValuePair<Guid, SimServiceReference.ModelData> selected = (KeyValuePair<Guid, SimServiceReference.ModelData>)lbConnectedModels.SelectedItem;
                if (selected.Value.FunctionstateModel == SimServiceReference.FunctionState.OK)
                {
                    _client.SetFunctionStateAtModel(selected.Key, SimServiceReference.FunctionState.Error);
                }
                if (selected.Value.FunctionstateModel == SimServiceReference.FunctionState.Error)
                {
                    _client.SetFunctionStateAtModel(selected.Key, SimServiceReference.FunctionState.OK);
                }
            }
            catch { }
        }
        private void rbControlType_Checked(object sender, RoutedEventArgs e)
        {
            if (ConnectedModels != null && ConnectedModels.Count >= 1)
            {
                RadioButton rb = sender as RadioButton;
                KeyValuePair<Guid, SimServiceReference.ModelData> selectedModel = (KeyValuePair<Guid, SimServiceReference.ModelData>)lbConnectedModels.SelectedItem;
                Thread selectedThread = ControlThreads[selectedModel.Key];

                if (rb.Content is SimServiceReference.SimControlType)
                {
                    SimServiceReference.SimControlType st = (SimServiceReference.SimControlType)rb.Content;
                    _client.SetControlTypeAtModel(selectedModel.Key, st);

                    if (st == SimServiceReference.SimControlType.Manual)
                    {
                        if (selectedThread.IsAlive)
                        {
                            selectedThread.Abort();
                        }
                    }
                    else if (st == SimServiceReference.SimControlType.Automatic1)
                    {
                        if (selectedThread.IsAlive)
                        {
                            selectedThread.Abort();
                            ControlThreads[selectedModel.Key] = new Thread(Automatic1);
                            ControlThreads[selectedModel.Key].Name = "Automatic1 - " + selectedModel.Key.ToString();
                            ControlThreads[selectedModel.Key].Priority = ThreadPriority.AboveNormal;
                            ControlThreads[selectedModel.Key].Start(selectedModel.Key);
                        }
                        else
                        {
                            ControlThreads[selectedModel.Key] = new Thread(Automatic1);
                            ControlThreads[selectedModel.Key].Start(selectedModel.Key);
                        }
                    }
                    else if (st == SimServiceReference.SimControlType.Automatic2)
                    {
                        if (selectedThread.IsAlive)
                        {
                            selectedThread.Abort();
                            ControlThreads[selectedModel.Key] = new Thread(Automatic2);
                            ControlThreads[selectedModel.Key].Start(selectedModel.Key);
                        }
                        else
                        {
                            ControlThreads[selectedModel.Key] = new Thread(Automatic2);
                            ControlThreads[selectedModel.Key].Start(selectedModel.Key);
                        }
                    }
                }
            }

        }
        private void AddNewModel_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            TryAddNewModel();
        }
        private void btnRetrySearchService_Click(object sender, RoutedEventArgs e)
        {
            DiscoverService();
        }
    }
}
